package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.CrawlAdapterFactory;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.exception.self.UnableParseRouteTypeException;
import com.foreveross.crawl.common.util.ClassUtils;
import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.crawl.util.StringUtil;
import com.foreveross.proxyip.ProxyIpModel;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.Maps;

/**
 * 一起飞 网站适配器 采用webClient (14年6月份网站改动，已经修复 。Bright) 由国际和国内的航班信息
 * 
 * @author guokenye
 * 
 */
public class YiqifeiAdapter extends AbstractAdapter {
	private static Logger log = LoggerFactory.getLogger(YiqifeiAdapter.class);

	public static final Long Yiqifei_GradChannel = 6L;

	private DefaultHttpClient httpClient;

	public YiqifeiAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	@SuppressWarnings("incomplete-switch")
	@Override
	public String getUrl() throws Exception {

		// 区分国内单程和国际往返
		switch (getRouteType()) {
		case DOMESTIC_ONEWAYTRIP:// 国内单程
		case INTERNATIONAL_ONEWAY:
		case INTERNATIONAL_ROUND:// 国际往返
			StringBuffer urls = new StringBuffer("http://www.yiqifei.com/AjaxData/Handler/ProcessUrl.ashx");
			return urls.toString();
		}
		return null;
	}

	@Override
	public Object fetch(String url) throws Exception {
		Object obj = null;
		try {
			long starttime = System.currentTimeMillis();
			switch (getRouteType()) {
			case DOMESTIC_ONEWAYTRIP:
				log.info("开始国内单程抓取...");
				obj = fetchDomesticOneWay();
				log.info("国内单程抓取完成：" + (System.currentTimeMillis() - starttime) + "MS");
				break;
			case DOMESTIC_ROUNDTRIP:
				log.debug("国内往返暂未实现！");
				throw new UnableParseRouteTypeException("一起飞", getRouteType().getName());
			case INTERNATIONAL_ONEWAY:
				log.info("开始国际单程抓取...");
				obj = fetchRound();
				log.info("国际单程抓取完成：" + (System.currentTimeMillis() - starttime) + "MS");
				break;
			case INTERNATIONAL_ROUND:
				log.info("开始国际往返抓取...");
				obj = fetchRound();
				log.info("国际往返抓取完成：" + (System.currentTimeMillis() - starttime) + "MS");
				break;
			}
		} finally {
			appendPageContents("java.util.List");
		}
		return obj;
	}

	/**
	 * 
	 * ====== 国内单程 BEGIN ==================================================================================*
	 * 
	 */

	public Object fetchDomesticOneWay() throws Exception {
		HttpClient client;
		HttpGet g;
		HttpPost p = null;
		String page;
		client = getHttpClient();
		Document doc;
		Map<String,String> params = Maps.newHashMap();
		try {
			//StringBuffer firstUrl = new StringBuffer("http://domfight.yiqifei.com/Ajax/Handler/ProcessUrl.ashx?op=searchTicket&airline=all&cabin=Y&t=");
			//firstUrl.append(this.taskQueue.getIsReturn() + "_" + this.taskQueue.getFromCity() + "_" + this.taskQueue.getToCity() + "_" + this.taskQueue.getFlightDate() + "_" + this.taskQueue.getReturnGrabDate());
			//g = new HttpGet(firstUrl.toString());
			params.put("airline", "all");
			params.put("cabin", "Y");
			//params.put("datatime", "Fri Aug 08 2014 10:40:02 GMT+0800");
			params.put("op", "searchTicket");
			String t = StringUtil.join("_", String.valueOf(taskQueue.getIsReturn()),taskQueue.getFromCity(),taskQueue.getToCity(),taskQueue.getFlightDate(),taskQueue.getReturnGrabDate());
			params.put("t", t);
			p= getBasePost("http://domfight.yiqifei.com/Ajax/Handler/ProcessUrl.ashx", params);
			page = excuteRequest(client, p, true);
			//g = new HttpGet("http://domfight.yiqifei.com/ShowFlights.aspx?t=" + t + "&isurl=1");
			g = new HttpGet("http://domfight.yiqifei.com/ShowFlights.aspx");
			page = excuteRequest(g);
			doc = Jsoup.parse(page);
			Elements ss = doc.select("[id^=yqf_]");
			if(ss.size() != 1){
				throw new Exception("一起飞又改了");
			}
			/*g = new HttpGet("http://domfight.yiqifei.com/Ajax/Handler/SearchFSInnerFlight.ashx?_=" + System.currentTimeMillis() + "&ss=" + ss.select("[id^=yqf_]").first().html());
			g.addHeader("Host","domfight.yiqifei.com");
			g.addHeader("Referer","http://domfight.yiqifei.com/ShowFlights.aspx?t=" + t + "&isurl=1");
			page = excuteRequest(client, g, true);*/
			/*int RspCode = JSONObject.fromObject(page).getInt("RspCode");
			if(RspCode != 0){
				throw new Exception("国内单程请求结果错误");
			}*/
			params.clear();
			params.put("ss", ss.select("[id^=yqf_]").first().html());
			String urlPara = RegHtmlUtil.regStr(page, "url: \"(.*?_f)\"");
			p = getBasePost("http://domfight.yiqifei.com/" + urlPara, params);
			p.addHeader("Host","domfight.yiqifei.com");
			p.addHeader("Referer","http://domfight.yiqifei.com/ShowFlights.aspx");
			page = excuteRequest(p);
			p = new HttpPost("http://domfight.yiqifei.com/Ajax/Pages/GetInnerFlight.aspx");
			page = excuteRequest(client, p, true);

			// 1:id；2:航空公司简码；3：航空公司名称4：航班号；5：机型；6：起飞时间；7：起飞机场；8：到达日期；9：到达机场；10：税费；11：价格；12：舱位;13：所有舱位
			List<List<String>> htmls = RegHtmlUtil.retrieveLinkss(page, getRegexCN());

			return htmls;
		} finally {
			if(p != null){
				p.releaseConnection();
			}
		}
	}
	
	// 更多舱位正则表达式。
	private String getRegexMoreCNCabin() {
		StringBuffer regex = new StringBuffer();
		regex.append("class=\"AirBuilding\">(.+?)</span>.*?");
		regex.append("class=\"CabinPrice\">(.+?)</span>.*?");
		regex.append("class=\"Linko7\">.*?<p>(.+?)</p>.*?");
		return regex.toString();
	}

	// 1:id；2:航空公司简码；3：航空公司名称4：航班号；5：机型；6：起飞时间；7：起飞机场；8：到达日期；9：到达机场；10：税费；11：价格；12：舱位;13：所有舱位
	public String getRegexCN() {
		StringBuffer regex = new StringBuffer();
		regex.append("id=\"flight_tr_(.+?)\">.*?");
		regex.append("<img\\s*src=\"http://airlineico.b0.upaiyun.com/([A-Z]{2})\\.png.*?");
		regex.append("class=\"AirlineName\">(.+?)</span>.*?");
		regex.append("class=\"FlightNo\">(.+?)</span>.*?");
		regex.append("class=\"PlaneType\">(.+?)</span>.*?");
		regex.append("class=\"DepartureTime\">(.+?)</span>.+?class=\"Departure.*?>(.+?)</span>.*?");
		regex.append("class=\"ArrivalTime\">(.+?)</span>.+?class=\"Arrival\">(.+?)</span>.*?");
		regex.append("class=\"AirBuilding\">(.+?)</span>.+?");
		regex.append("class=\"CabinPrice\">(.+?)</span>.+?");
		regex.append("class=\"Linko7\">.*?<p>(.+?)</p>.*?</td>");
		regex.append("(.+?(?=class=\"even\"|</tbody>))");
		return regex.toString();
	}

	// 解析国内单程数据
	@SuppressWarnings("unchecked")
	public List<Object> paraseDomesticOneWay(Object fetchObject) throws Exception {
		List<List<String>> htmls = (List<List<String>>) fetchObject;
		List result = new ArrayList<AbstractPlaneInfoEntity>();
		AbstractPlaneInfoEntity plane = null;
		for (List<String> html : htmls) {
			if (this.isNeedCarrierKey(html.get(2))) {
				// 1:id；2:航空公司简码；3：航空公司名称4：航班号；5：机型；6：起飞时间；7：起飞机场；8：到达日期；9：到达机场；10：税费；11：价格；12：舱位;13：所有舱位
				if (html == null || html.size() == 0) {
					plane = new SinglePlaneInfoEntity();
				}
				plane = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, html.get(2), html.get(3), html.get(3), html.get(6), html.get(8), html.get(4), null, "一起飞", null, html.get(5));
				// 设置默认舱位
				String cabinName = html.get(12).replaceAll("\\s", "");
				CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, html.get(10), html.get(11), null, null, null, CabinEntity.class);
				PlaneInfoEntityBuilder.getSingleEntity(plane).getCabins().add(cabin);

				// 设置更多舱位
				ArrayList<List<String>> cabins = RegHtmlUtil.retrieveLinkss(html.get(13), this.getRegexMoreCNCabin());
				for (List<String> cb : cabins) {
					cabinName = cb.get(3).replaceAll("\\s", "");
					CabinEntity mcabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, cb.get(1), cb.get(2), null, null, null, CabinEntity.class);
					PlaneInfoEntityBuilder.getSingleEntity(plane).getCabins().add(mcabin);
				}
				result.add(plane);
			}
		}
		PlaneInfoEntityBuilder.buildLimitPrice(result);
		return result;
	}

	/**
	 * 
	 * ====== 国内单程 END ==================================================================================*
	 * 
	 */

	// 抓取国际往返数据
	private Object fetchRound() throws Exception {
		/**
		 * planeInfo信息 ：1、票价；2、税费；3、总价；4、航空公司编码；5、航空公司简称加编码；6、一起飞航班id； 7、起飞时间；8、到达时间（含加天数）；9、去程机场地址；10、去程抵达机场地址； 11、回程起飞时间；12、回程到达时间（含加天数）；13、回程机场地址；14、回程抵达机场地址 15、舱位等级；16、下一页信息
		 */
		List<List<String>> planeInfos = new ArrayList<List<String>>();

		List<Integer> ks = new ArrayList<Integer>();
		for (int k = 1; k < 4; k++) {
			ks.add(k);
		}
		Method method = this.getClass().getDeclaredMethod("callLoadIntelPlaneInfos", List.class, List.class);
		List<Boolean> list = ClassUtils.openThreads(ks, this, method, 3, 12000000L, planeInfos);
		boolean executeBool = false;// 执行结果
		for (Boolean bool : list) {
			executeBool |= bool;
		}
		if (!executeBool) {
			logger.info("planeInfos:" + planeInfos.size());
			throw new Exception("找不到相应的航班信息");
		}
		if (planeInfos.size() == 0) {
			throw new FlightInfoNotFoundException();
		}
		return planeInfos == null ? null : planeInfos;
	}

	/**
	 * 等待加载所有航班完全
	 * 
	 * @param cookieUrl
	 * @param tempList
	 * @return
	 * @throws Exception
	 */
	private DefaultHttpClient waitLoadIntelPlaneInfo(String cookieUrl, List<List<String>> tempList) throws Exception {
		HttpPost request = null;
		boolean ctrl = true;
		DefaultHttpClient client = null;
		long beginTime = System.currentTimeMillis();
		String html = "";

		while (ctrl && System.currentTimeMillis() - beginTime < 600000)
			try {
				boolean again = false;
				client = this.setCookie(cookieUrl);
				request = new HttpPost("http://www.yiqifei.com/AjaxData/Handler/SearchFS_SyscV2.ashx?search=2");
				String jobId = excuteRequest(client, request, true);
				request.releaseConnection();
				Thread.sleep(500);
				boolean bool = true;
				StringBuffer regexString = getRegexString();
				Long beginTime2 = System.currentTimeMillis();
				while (bool && System.currentTimeMillis() - beginTime2 < 300000) {
					request = new HttpPost("http://www.yiqifei.com/AjaxData/Handler/SearchFS_SyscV2.ashx?search=2&Jobid=" + jobId);
					html = excuteRequest(client, request, true);
					// System.out.println(html);
					request.releaseConnection();
					Thread.sleep(2000);
					if (StringUtils.startsWith(html, "true")) {
						// System.out.println("finish");
						bool = false;
					}
					if (StringUtils.startsWith(html, "false||")) {
						again = true;
						bool = false;

					}
				}
				if (again) {
					continue;
				}
				request.releaseConnection();

				Thread.sleep(6000);
				request = new HttpPost("http://www.yiqifei.com/AjaxData/Handler/ShowTicket.ashx?FlyTime=&FlyType=&Source=&airlinc=&ajax=true&b_FlyTime=&b_FlyType=&pageIndex=1&pageSize=1000&sort=");
				html = excuteRequest(client, request, true);
				if (tempList != null) {
					List list = RegHtmlUtil.retrieveLinkss(html, regexString.toString());
					if (list != null && list.size() != 0) {
						tempList.addAll(list);
					}
				}
				if (tempList != null && tempList.size() == 0) {
					continue;
				}
				ctrl = false;
				return client;
			} catch (Exception e) {
				logger.error("waitLoadIntelPlaneInfo:" + cookieUrl);
				logger.error(e);
			} finally {
				if (request != null) {
					request.releaseConnection();
				}
			}
		return client;
	}

	/**
	 * 删除不需要的航空公司航班，顺便加载舱位类型
	 * 
	 * @param planeinfos
	 */
	private List<List<String>> getNeedPlaneinfo(List<List<String>> planeinfos, Integer cabinLevel) {
		List<List<String>> results = new ArrayList<List<String>>();
		for (List<String> planeinfo : planeinfos) {
			if (this.isNeedCarrierKey(planeinfo.get(4))) {
				if (cabinLevel != null) {
					planeinfo.add(cabinLevel + "");// 舱位类型加载进去,//剔除不需要的航线
				}
				results.add(planeinfo);
			}
		}
		return results;
	}

	/**
	 * group 解释对应意义：1、票价；2、税费；3、总价；4、航空公司编码；5、航空公司简称加编码；6、一起飞航班id； 7、起飞时间；8、到达时间（含加天数）；9、去程机场地址；10、去程抵达机场地址； 11、回程起飞时间；12、回程到达时间（含加天数）；13、回程机场地址；14、回程抵达机场地址
	 * 
	 * @return
	 */
	private StringBuffer getRegexString() {
		StringBuffer regexString = new StringBuffer("<table class=\"f_list_table\">.+?");

		// 票价
		regexString.append("class=\"price11\">.*?(\\d+?)</strong></span></div>.+?");
		// 税费
		regexString.append("class=\"price10\">.*?(\\d+?)</strong></span></div>.+?");
		// 总价
		regexString.append("class=\"price9\">.*?(\\d+?)</strong></span></div>.+?");

		regexString.append("<table class=\"f_list_trip_table\">.+?");

		// 航空公司
		regexString.append("<span class=\"fly-ico pubFlights_(.+?)\"></span>(.+?)</li>.+?");

		// 航班id
		regexString.append("showticket2\\.aspx\\?id=(\\d+).+?");

		// 起飞时间 到达时间
		regexString.append("class=\"f_list_trip_td2\".+?(\\d{1,2}:\\d{1,2})\\s*?<p>.+?(\\d{1,2}:\\d{1,2}\\+??\\d??)\\s*?</p>");
		// 去程机场地址
		regexString.append(".+?class=\"cityname\">\\s*?(.+?)\\s*?</em>");
		// 到达机场地址
		regexString.append(".+?class=\"cityname\">\\s*?(.+?)\\s*?</em>.+?");
		// 返程
		if (this.taskQueue.getIsReturn() == 1) {
			regexString.append("<tr>.+?</tr>");// 返程航班渠道信息

			// 起飞时间 到达时间
			regexString.append(".+?class=\"f_list_trip_td2\".+?(\\d{1,2}:\\d{1,2})\\s*?<p>.+?(\\d{1,2}:\\d{1,2}\\+??\\d??)\\s*?</p>");
			// 去程机场地址
			regexString.append(".+?class=\"cityname\">\\s*?(.+?)\\s*?</em>");
			// 到达机场地址
			regexString.append(".+?class=\"cityname\">\\s*?(.+?)\\s*?</em>.+?");
		}
		// regexString.append("<span class=\"fly-ico pubFlights_(.+?)\"></span>(.+?)</li>.+?");
		//
		// //航班id
		// regexString.append("showticket2\\.aspx\\?id=(\\d+).+?");
		//
		// //起飞时间 到达时间
		// regexString.append("class=\"f_list_trip_td2\".+?(\\d{1,2}:\\d{1,2})\\s*?<p>.+?(\\d{1,2}:\\d{1,2}\\+??\\d??)\\s*?</p>");

		regexString.append("</table>.+?</table>");
		return regexString;
	}

	@Override
	public boolean validateFetch(Object fetchObject) throws Exception {
		if (fetchObject == null) {
			throw new Exception("源网页数据为空或数据抓取不正确！");
		}
		return true;
	}

	@SuppressWarnings("incomplete-switch")
	@Override
	public List<Object> paraseToVo(Object fetchObject) throws Exception {
		long starttime = System.currentTimeMillis();
		List<Object> list = null;
		switch (getRouteType()) {
		case DOMESTIC_ONEWAYTRIP:
			log.info("解析国内单程...");
			list = paraseDomesticOneWay(fetchObject);
			log.info("国内单程解析完成：" + (System.currentTimeMillis() - starttime) + "MS");
			break;
		case INTERNATIONAL_ONEWAY:
		case INTERNATIONAL_ROUND:
			log.info("解析国际往返...");
			list = paraseRound(fetchObject);
			log.info("国际往返解析完成：" + (System.currentTimeMillis() - starttime) + "MS");
		}
		return list;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<Object> paraseRound(Object fetchObject) throws Exception {
		List<List<String>> planeInfos = (List<List<String>>) fetchObject;
		// ReturnDoublePlaneInfoEntity
		Map<String, AbstractPlaneInfoEntity> goCabinMap = new HashMap<String, AbstractPlaneInfoEntity>();

		Map<String, ReturnDoublePlaneInfoEntity> returnCabinMap = new HashMap<String, ReturnDoublePlaneInfoEntity>();

		Map<String, Set<String>> goReturnR = new HashMap<String, Set<String>>();

		/**
		 * planeInfo信息 ：1、票价；2、税费；3、总价；4、航空公司编码；5、航空公司简称加编码；6、一起飞航班id； 7、起飞时间；8、到达时间（含加天数）；9、去程机场地址；10、去程抵达机场地址； 11、回程起飞时间；12、回程到达时间（含加天数）；13、回程机场地址；14、回程抵达机场地址 15、舱位等级；16、下一页信息
		 */
		for (int i = 0; i < planeInfos.size(); i++) {
			try {
				int pageI = this.taskQueue.getIsReturn() == 1 ? 16 : 12;
				int cabinLevel = this.taskQueue.getIsReturn() == 1 ? 15 : 11;
				List<String> planeInfo = planeInfos.get(i);
				String nextPage = planeInfo.get(pageI);
				Set<String> returnNos = new HashSet<String>();// 去程航班对应返程信息

				String regex = "<table class=\"rp_TicketFligh\".+?<tr>.+?</tr>\\s*?(<tr class=\"FlightDataListTR\".+?>.+?)</table>";// 排除第一行，获取下一页table航班信息
				List<String> tables = RegHtmlUtil.retrieveLinks(nextPage, regex, 1);// 往返tables大小为2，单程（目前业务无需求）大小为1
				if (this.taskQueue.getIsReturn() == 1 && tables.size() != 2) {
					throw new Exception("一起飞id+" + planeInfo.get(6) + "+不存在返程航班");
				}
				regex = "<tr class=\"FlightDataListTR\".+?<td.*?>(.+?)</td>\\s*?<td.*?>(.+?)</td>\\s*?<td.*?>(.+?)</td>\\s*?<td.*?>(.+?)</td>\\s*?<td.*?>(.+?)</td>\\s*?<td.*?>(.+?)</td>.*?</tr>";// 取前两列
				Set<CabinRelationEntity> cabinRelations = new HashSet<CabinRelationEntity>();
				if (this.taskQueue.getIsReturn() == 1) {// 往返

					String returnTripStr = tables.get(1);
					// System.out.println("return :"+returnTripStr);
					List<List<String>> returnTrips = RegHtmlUtil.retrieveLinkss(returnTripStr, regex);
					for (List<String> row : returnTrips) {
						String planeNo = row.get(1);
						String type = this.getCell(StringUtils.trim(row.get(6))).get(0);
						List<String> planeNos = this.getCell(planeNo);
						ReturnCabinEntity cabinEntity = new ReturnCabinEntity();
						cabinEntity.setCabinType(this.getCabinType(planeInfo.get(cabinLevel)));
						returnNos.add(planeNos.get(0));
						this.assortPlaneInfo(planeNos.get(0), type, returnCabinMap, planeInfo, cabinEntity, 1);

						ReturnDoublePlaneInfoEntity returnPlaneInfo = (ReturnDoublePlaneInfoEntity) returnCabinMap.get(planeNos.get(0));

						returnPlaneInfo.setReturnTransits(this.getReturnTransitSet(row));

						CabinRelationEntity cabinRelation = new CabinRelationEntity();// 返程关系
						cabinRelation.setFullPrice(Double.parseDouble(planeInfo.get(1))); // 票价
						cabinRelation.setTaxesPrice(Double.parseDouble(planeInfo.get(2)));// 税价
						cabinRelation.setTotalFullPrice(Double.parseDouble(planeInfo.get(3)));// 总价
						cabinRelation.setReturnCabinId(cabinEntity.getId());
						cabinRelations.add(cabinRelation);

						// if(returnCabinMap.get(planeNos.get(0)==null)){
						// AbstractPlaneInfoEntity entity = PlaneInfoEntityBuilder.getReturnTrip(this.taskQueue,
						// planeInfo.get(4),
						// planeInfo.get(5),
						// planeInfo.get(5),
						// planeInfo.get(11),
						// planeInfo.get(12), planeNos.get(0), planeInfo.get(3), "一起飞", null, type);//最低价属性先保存当前舱位价格
						// }
					}
					// returnMap.put(key, value);

				}

				String go = tables.get(0);
				List<List<String>> goCell = RegHtmlUtil.retrieveLinkss(go, regex);
				for (List<String> row : goCell) {
					String planeNo = row.get(1);
					String type = this.getCell(StringUtils.trim(row.get(6))).get(0);
					List<String> planeNos = this.getCell(planeNo);

					// AbstractPlaneInfoEntity entity = PlaneInfoEntityBuilder.buildPlaneInfo(this.taskQueue,
					// planeInfo.get(4),
					// planeInfo.get(5),
					// planeInfo.get(5),
					// planeInfo.get(7),
					// planeInfo.get(8), planeNos.get(0), planeInfo.get(3), "一起飞", null, type);//最低价属性先保存当前舱位价格

					com.foreveross.crawl.domain.airfreight.CabinEntity cabin = new com.foreveross.crawl.domain.airfreight.CabinEntity();
					if (!StringUtils.startsWith(planeNos.get(0), planeInfo.get(4))) {
						// System.out.println("k:" + planeInfo.get(cabinLevel) + ";key:" + planeInfo.get(4) + ";nextPage:" + planeNos.get(0) + ";价格：" + planeInfo.get(1) + ";id:" + planeInfo.get(6));
						continue;
					}
					this.assortPlaneInfo(planeNos.get(0), type, goCabinMap, planeInfo, cabin, 0);
					if (this.taskQueue.getIsReturn() == 1) {
						for (CabinRelationEntity relation : cabinRelations) {
							relation.setCabinId(cabin.getId());// 设置舱位关系
						}
						DoublePlaneInfoEntity doublePlane = (DoublePlaneInfoEntity) goCabinMap.get(planeNos.get(0));
						this.assortGoReturnR(goReturnR, planeNos.get(0), returnNos);// 划分往返关系 去程对应多个返程航班
						cabin.setCabinType(this.getCabinType(planeInfo.get(cabinLevel)));
						cabin.setPlaneInfoEntity(doublePlane);
						doublePlane.setTransits(getTransitSet(row));
						// doublePlane.getCabins().add(cabin);
						doublePlane.getCabinRelations().addAll(cabinRelations);// 其它类型舱位可以依旧存在
					} else {
						SinglePlaneInfoEntity singlePlane = (SinglePlaneInfoEntity) goCabinMap.get(planeNos.get(0));
						cabin.setPrice(Double.parseDouble(planeInfo.get(1)));
						cabin.setCabinType(this.getCabinType(planeInfo.get(cabinLevel)));
						cabin.setPlaneInfoEntity(singlePlane);
						// singlePlane.getCabins().add(cabin);
						singlePlane.setTransits(getTransitSet(row));
					}
					// this.assortPlaneInfo(goCabinMap, planeNo);//航班舱位分类
				}
			} catch (Exception e) {
				/* e.printStackTrace(); */
			}
		}
		if (this.taskQueue.getIsReturn() == 1) {
			Set<String> set = goCabinMap.keySet();
			for (String planeNo : set) {
				DoublePlaneInfoEntity entity = (DoublePlaneInfoEntity) goCabinMap.get(planeNo);
				Set<String> returnNos = goReturnR.get(planeNo);
				for (String returnNo : returnNos) {
					ReturnDoublePlaneInfoEntity returnPlaneInfo = returnCabinMap.get(returnNo);
					if (returnPlaneInfo != null) {
						entity.getReturnPlaneInfos().add(returnPlaneInfo);
					}
				}
			}
		}
		List result = new ArrayList<Object>(goCabinMap.values());
		PlaneInfoEntityBuilder.buildLimitPrice(new ArrayList<AbstractPlaneInfoEntity>(result));
		return result;
	}

	// 获取中转
	private ArrayList<TransitEntity> getTransitSet(List<String> row) {
		ArrayList<TransitEntity> transits = new ArrayList<TransitEntity>();

		List<String> planeNos = this.getCell(StringUtils.trim(row.get(1)));
		List<String> fromAirPortNames = this.getCell(StringUtils.trim(row.get(2)));
		List<String> toAirPortNames = this.getCell(StringUtils.trim(row.get(3)));
		List<String> startTimes = this.getCell(StringUtils.trim(row.get(4)));
		List<String> endTimes = this.getCell(StringUtils.trim(row.get(5)));
		List<String> types = this.getCell(StringUtils.trim(row.get(6)));

		if (planeNos != null && planeNos.size() > 1) {
			for (int i = 0; i < planeNos.size(); i++) {
				TransitEntity transit = new TransitEntity();
				transit.setFlightType(types.get(i));
				transit.setFlightNo(planeNos.get(i));
				transit.setFromAirPortName(fromAirPortNames.get(i));
				transit.setToAirPortName(toAirPortNames.get(i));
				transit.setStartTime(CrawlAdapterFactory.getFlightTime(taskQueue.getFlightDate(), startTimes.get(i)));
				transit.setEndTime(CrawlAdapterFactory.getFlightTime(taskQueue.getFlightDate(), endTimes.get(i)));
				transits.add(transit);
			}
		}
		return transits;
	}

	/**
	 * 获取返程重装航班
	 * 
	 * @param row
	 * @return
	 */
	private ArrayList<ReturnTransitEntity> getReturnTransitSet(List<String> row) {
		/**
		 * planeInfo信息 ：1、票价；2、税费；3、总价；4、航空公司编码；5、航空公司简称加编码；6、一起飞航班id； 7、起飞时间；8、到达时间（含加天数）；9、去程机场地址；10、去程抵达机场地址； 11、回程起飞时间；12、回程到达时间（含加天数）；13、回程机场地址；14、回程抵达机场地址 15、舱位等级；16、下一页信息
		 */
		ArrayList<ReturnTransitEntity> transits = new ArrayList<ReturnTransitEntity>();
		String planeNo = row.get(1);
		List<String> planeNos = this.getCell(StringUtils.trim(row.get(1)));
		List<String> planeNames = this.getCell(StringUtils.trim(row.get(2)));
		List<String> types = this.getCell(StringUtils.trim(row.get(6)));

		if (planeNos != null && planeNos.size() > 1) {
			for (int i = 1; i < planeNos.size(); i++) {
				ReturnTransitEntity transit = new ReturnTransitEntity();
				transit.setFlightType(types.get(i));
				transit.setFlightNo(planeNos.get(i));
				transit.setFromAirPortName(planeNames.get(i));
				transits.add(transit);
			}
		}
		return transits;
	}

	/**
	 * 划分去程回程关系 ，（一个航班一天只会起飞一次）
	 * 
	 * @param goReturnR
	 * @param goNo
	 * @param returnNo
	 */
	private void assortGoReturnR(Map<String, Set<String>> goReturnR, String goNo, String returnNo) {
		if (goReturnR.containsKey(goNo)) {
			goReturnR.get(goNo).add(returnNo);
		} else {
			Set<String> set = new HashSet<String>();
			set.add(returnNo);
			goReturnR.put(goNo, set);
		}
	}

	/**
	 * 划分往返关系 去程对应多个返程航班
	 * 
	 * @param goReturnR
	 * @param goNo
	 * @param returnNos
	 */
	private void assortGoReturnR(Map<String, Set<String>> goReturnR, String goNo, Set<String> returnNos) {
		if (goReturnR.containsKey(goNo)) {

			for (String returnNo : returnNos) {
				goReturnR.get(goNo).add(returnNo);
			}
		} else {
			Set<String> set = new HashSet<String>();
			for (String returnNo : returnNos) {
				set.add(returnNo);
			}
			goReturnR.put(goNo, set);
		}
	}

	/**
	 * 航班分类
	 * 
	 * @param planeNo
	 * @param type
	 * @param map
	 * @param planeInfo
	 * @param cabin
	 * @param isReturn
	 */
	private void assortPlaneInfo(String planeNo, String type, Map map, List<String> planeInfo, Object cabin, int isReturn) {
		if (isReturn == 1) {
			ReturnCabinEntity cabinEntity = (ReturnCabinEntity) cabin;
			ReturnDoublePlaneInfoEntity entity = null;
			if (map.containsKey(planeNo)) {
				entity = (ReturnDoublePlaneInfoEntity) map.get(planeNo);
			} else {
				entity = (ReturnDoublePlaneInfoEntity) PlaneInfoEntityBuilder.getReturnTrip(this.taskQueue, planeInfo.get(4), planeInfo.get(5), planeInfo.get(5), planeInfo.get(11), planeInfo.get(12), planeNo, planeInfo.get(1), "一起飞", null, type);// 最低价属性先保存当前舱位价格
				map.put(planeNo, entity);
			}
			entity.getReturnCabins().add(cabinEntity);
		} else {
			com.foreveross.crawl.domain.airfreight.CabinEntity cabinEntity = (com.foreveross.crawl.domain.airfreight.CabinEntity) cabin;
			if (this.taskQueue.getIsReturn() == 1) {
				DoublePlaneInfoEntity entity = null;
				if (map.containsKey(planeNo)) {
					entity = (DoublePlaneInfoEntity) map.get(planeNo);

				} else {

					entity = (DoublePlaneInfoEntity) PlaneInfoEntityBuilder.buildPlaneInfo(this.taskQueue, planeInfo.get(4), planeInfo.get(5), planeInfo.get(5), planeInfo.get(7), planeInfo.get(8), planeNo, planeInfo.get(1), "一起飞", null, type);// 最低价属性先保存当前舱位价格
					map.put(planeNo, entity);
				}
				entity.getCabins().add(cabinEntity);
			} else {
				SinglePlaneInfoEntity entity = null;
				if (map.containsKey(planeNo)) {
					entity = (SinglePlaneInfoEntity) map.get(planeNo);
				} else {
					entity = (SinglePlaneInfoEntity) PlaneInfoEntityBuilder.buildPlaneInfo(this.taskQueue, planeInfo.get(4), planeInfo.get(5), planeInfo.get(5), planeInfo.get(7), planeInfo.get(8), planeNo, planeInfo.get(1), "一起飞", null, type);// 最低价属性先保存当前舱位价格
					map.put(planeNo, entity);
				}
				entity.getCabins().add(cabinEntity);
			}
		}
	}

	private List<String> getCell(String planeNoOrName) {
		List<String> result = new ArrayList<String>();
		String[] cells = StringUtils.splitByWholeSeparator(planeNoOrName, "<p>");
		for (String cell : cells) {
			String a = StringUtils.replace(cell, "</p>", "");
			a = StringUtils.replace(a, "&nbsp;", "");
			a = a.replaceAll("<.+?>", "");
			if (StringUtils.isNotEmpty(a)) {
				result.add(StringUtils.trim(a));
			}
		}
		return result;
	}

	private String getCabinType(String cabinLevel) {
		if ("1".equals(cabinLevel)) {
			return "经济舱";
		}
		if ("2".equals(cabinLevel)) {
			return "公务舱";
		}
		if ("3".equals(cabinLevel)) {
			return "头等舱";
		}
		return "";
	}

	/**
	 * 根据一个航空简称来获得对应的carrierKEY 如 深航>>SC
	 * 
	 * @param carrierName
	 * @return
	 */
	private String getCarrierKey(String carrierName) {
		if (carrierName == null) {
			return null;
		}
		// else if (carrierName.matches(".*中国联合.*")) {// 东方航空公司 前身
		// return "MU";
		// }
		else if (carrierName.matches(".*海航.*")) {
			return "HU";
		} else if (carrierName.matches(".*南航.*")) {
			return "CZ";
		} else if (carrierName.matches(".*国航.*")) {
			return "CA";
		} else if (carrierName.matches(".*山航.*")) {// 山东航空公司
			return "SC";
		} else if (carrierName.matches(".*深航.*")) {
			return "ZH";
		} else if (carrierName.matches(".*东航.*")) {// 东方航空公司
			return "MU";
		} else if (carrierName.matches(".*川航.*")) {
			return "3C";
		} else if (carrierName.matches(".*厦航.*")) {
			return "MF";
		} else {
			return null;
		}
	}

	private boolean isNeedCarrierKey(String carrierKey) {
		String[] keys = { "MU", "HU", "CZ", "CA", "SC", "ZH", "3C", "MF" };
		List list = Arrays.asList(keys);
		return list.contains(carrierKey);
	}

	/**
	 * 通过url提交基础参数，保存cookie
	 * 
	 * @param url
	 * @return httpClient
	 * @throws Exception
	 */
	private DefaultHttpClient setCookie(String url) throws Exception {
		int status = 0;
		HttpEntity entity = null;
		HttpPost request = null;
		DefaultHttpClient httpClient = null;
		Long start = System.currentTimeMillis();
		String str = RegHtmlUtil.regStr(url, ".*?cabin=([\\w])", 1);
		url = RegHtmlUtil.regStr(url, "^(.*?).cabin=", 1);

		while (status != 200 && System.currentTimeMillis() - start < 200000) {
			try {
				httpClient = (DefaultHttpClient)getHttpClient();// 八秒钟
				Map<String, String> params = Maps.newConcurrentMap();
				params.put("op", "searchTicket");
				params.put("airline", "");
				params.put("cabin", str);
				params.put("dccode", "");
				params.put("showType", "1");
				params.put("t", this.taskQueue.getIsReturn() + "_" + this.taskQueue.getFromCity() + "_" + this.taskQueue.getToCity() + "_" + this.taskQueue.getFlightDate() + "_" + this.taskQueue.getReturnGrabDate());
				request = super.getBasePost(url, params);
				HttpResponse response = httpClient.execute(request);

				status = response.getStatusLine().getStatusCode();
				entity = response.getEntity();
			} catch (Exception e) {
				/* e.printStackTrace(); */
				if(this.isUseProxyip()) {
					proxyIp.setUseStatus(ProxyIpModel.UNENBLE);
				}
			} finally {
				EntityUtils.consume(entity);
				if (request != null) request.releaseConnection();
				if(this.isUseProxyip()) {
					ipProvider.rollBackProxyIp(proxyIp);
				}
			}
		}
		return httpClient;
	}


	/**
	 * 反射一个线程获取一个国际航班类型的航班
	 * 
	 * @param ks
	 * @param planeInfos
	 */
	public Boolean callLoadIntelPlaneInfos(List<Integer> ks, List<List<String>> planeInfos) {
		String cookieUrl = null;
		try {
			Integer k = ks.get(0);
			List<List<String>> tempList = new ArrayList<List<String>>();
			cookieUrl = getUrl() + "?cabin=" + k;
			DefaultHttpClient client = waitLoadIntelPlaneInfo(cookieUrl, tempList);
			log.info("舱位：" + k + " 抓取数量:" + tempList.size());
			if (tempList == null || tempList.size() == 0) {
				return false;
			}
			tempList = this.getNeedPlaneinfo(tempList, k);
			List<List<String>> needList = new ArrayList<List<String>>();
			Method method = this.getClass().getDeclaredMethod("setIntelPlaneInfo", List.class, List.class, String.class, List.class, List.class);
			// wDefaultHttpClient client = waitLoadIntelPlaneInfo(cookieUrl,null);
			ArrayList clientContext = new ArrayList();
			clientContext.add(client);
			clientContext.add(System.currentTimeMillis());// client记录执行时间，因为一起飞下一步过段时间id失效
			ClassUtils.invokeMethods(tempList, this, method, 5, needList, getUrl() + "?cabin=" + k, clientContext, tempList);
			if (needList != null && needList.size() != 0) {
				planeInfos.addAll(needList);
			}
			return true;
		} catch (Exception e) {
			logger.error("callLoadIntelPlaneInfos:" + cookieUrl);
			logger.error(e);
			return false;
		}
	}

	/**
	 * 设置一个国际航班的下一页信息
	 * 
	 * @param planeInfo
	 * @param needList
	 * @param cookieurl
	 * @param clientContext
	 * @throws Exception
	 */
	public void setIntelPlaneInfo(List<String> planeInfo, List<List<String>> needList, String cookieurl, List clientContext, List<List<String>> tempList) throws Exception {
		DefaultHttpClient client = (DefaultHttpClient) clientContext.get(0);

		HttpPost request = null;
		try {
			String url = "http://www.yiqifei.com/AjaxData/Handler/GetShowTicket2.ashx?_=" + System.currentTimeMillis() + "&ajax=true&id=" + planeInfo.get(6) + "&getflight=true";
			request = new HttpPost(url);
			String resHtml = excuteRequest(client, request, true);
			if (StringUtils.isNotBlank(resHtml) && !StringUtils.equals(resHtml, "false")) {
				planeInfo.add(resHtml);
				needList.add(planeInfo);
			}

		} catch (Exception e) {
			this.logger.error("setIntelPlaneInfo:" + cookieurl);
			this.logger.error(e);

			reSetIntelClient(cookieurl, clientContext, tempList);

			throw e;
		} finally {
			if (request != null) {
				request.releaseConnection();
			}
		}
	}

	private void reSetIntelClient(String cookieurl, List clientContext, List<List<String>> tempList) throws Exception {
		DefaultHttpClient client;
		List<List<String>> newTempList = new ArrayList<List<String>>();
		client = this.waitLoadIntelPlaneInfo(cookieurl, newTempList);

		newTempList = this.getNeedPlaneinfo(newTempList, null);
		this.resetPlaneInfoId(tempList, newTempList);
		clientContext.clear();
		clientContext.add(client);
		clientContext.add(System.currentTimeMillis());
	}

	private void resetPlaneInfoId(List<List<String>> tempList, List<List<String>> newTempList) throws Exception {
		Integer[] keyFields = { 4, 1 };
		Map<String, List> newTempMap = ClassUtils.getKeyMapByList(tempList, keyFields);

		for (List<String> newPlaneInfo : newTempList) {
			String key = newPlaneInfo.get(4) + "_" + newPlaneInfo.get(1);

			List<String> proessList = newTempMap.get(key);// 运行流程里面的对象
			if (proessList != null) {/*
									 * System.out.println("key:"+key+";proessListId:"+proessList.get(6)+";newList:"+newPlaneInfo.get(6));
									 */
				proessList.set(6, newPlaneInfo.get(6));
			}
		}
	}

}
